#pragma once

#include <iostream>
#include <stack>
#include <string>

class Receiver
{
public:
    Receiver(){};
    Receiver(const Receiver *x)
    {
        this->m_textContent = x->m_textContent;
    };
    virtual ~Receiver(){};

public:
    void sayHelloWorld()
    {
        m_textContent += "Hello, World!\n";
    }

    void sayBro666()
    {
        m_textContent += "老铁, 666!\n";
    }

    void sayNiubility()
    {
        m_textContent += "卧槽, 牛逼!\n";
    }

    void undoCommand(const Receiver *x)
    {
        this->m_textContent = x->m_textContent;
    }

    void printTextContent()
    {
        std::cout << "*************** content ***************" << std::endl;
        std::cout << m_textContent << std::endl;
    }

private:
    std::string m_textContent;
};

class Command
{
public:
    virtual ~Command()
    {
        delete m_copyback;
    };

public:
    virtual void execute() = 0;

    void backup(Receiver *receiver)
    {
        m_receiver = receiver;
        m_copyback = new Receiver(receiver);
    }
    void undo()
    {
        m_receiver->undoCommand(m_copyback);
    }

private:
    Receiver *m_receiver;
    Receiver *m_copyback;
};

class HelloCommand : public Command
{
public:
    HelloCommand(Receiver *receiver) : m_receiver(receiver){};

public:
    void execute() override
    {
        this->backup(m_receiver);
        m_receiver->sayHelloWorld();
    }

private:
    Receiver *m_receiver;
};

class Bro666Command : public Command
{
public:
    Bro666Command(Receiver *receiver) : m_receiver(receiver){};

public:
    void execute() override
    {
        this->backup(m_receiver);
        this->m_receiver->sayBro666();
    }

private:
    Receiver *m_receiver;
};

class NiubilityCommand : public Command
{
public:
    NiubilityCommand(Receiver *receiver) : m_receiver(receiver){};

public:
    void execute() override
    {
        this->backup(m_receiver);
        this->m_receiver->sayNiubility();
    }

private:
    Receiver *m_receiver;
};

class CommandHistory
{
public:
    void push(Command *command)
    {
        m_stackCommand.push(command);
    }

    Command *pop()
    {
        if (m_stackCommand.empty()) {
            return nullptr;
        }
        Command *command = m_stackCommand.top();
        m_stackCommand.pop();
        return command;
    }

private:
    std::stack<Command *> m_stackCommand;
};

class Invoker
{
public:
    void executeCommand(Command *command)
    {
        command->execute();
        m_cmdHistory.push(command);
    }
    void undo()
    {
        Command *cmd = m_cmdHistory.pop();
        if (cmd != nullptr) {
            cmd->undo();
            delete cmd;
        }
    }

private:
    CommandHistory m_cmdHistory;
};